const Proxy = require('../proxy')
const webDataUtil = require('../util/webDataUtil')
const reqlib = require('app-root-path').require
const btbAs = reqlib('/app/common/btb')
const appConfig = reqlib('/config/index')
const numberUtil = reqlib('/app/util/numberUtil')
const moment = require('moment')
const binanceProxy = reqlib('/app/common/binanceProxy')

const BtbIndex = Proxy.BtbIndex
const BtbRecord = Proxy.BtbRecord
const BtbMonitor = Proxy.BtbMonitor
const QuoteVolume = Proxy.QuoteVolume
const BtbWarnProxy = Proxy.BtbWarn

function parseValue (val) {
  return parseFloat(val || 0) || 0
}

exports.getBtbIndexByPage = async function (query, paging) {
  // 新创建的在前面
  const opt = {
    skip: paging.start,
    limit: paging.offset,
    sort: '-status',
    lean: true
  }
  const queryNew = {
    status: 1
  }
  if (query.code) {
    queryNew.code = query.queryNew
  }
  let list = await BtbIndex.find(queryNew, opt)
  list = list.filter((v) => {
    return v.xh_status === 1 || v.hy_status === 1
  })
  const futuresDailyRes = await webDataUtil.binance('getFuturesDaily')
  const futuresDailyData = futuresDailyRes.data || {}
  list.forEach((btbItem) => {
    const monthEmaInfo = {
      ...(btbItem.monthEmaInfo || {})
    }
    delete monthEmaInfo.emInfoList
    btbItem.monthEmaInfo = monthEmaInfo
    const weekEmaInfo = {
      ...(btbItem.weekEmaInfo || {})
    }
    delete weekEmaInfo.emInfoList
    btbItem.weekEmaInfo = weekEmaInfo
    const dayEmaInfo = {
      ...(btbItem.dayEmaInfo || {})
    }
    delete dayEmaInfo.emInfoList
    btbItem.dayEmaInfo = dayEmaInfo
    const hour4EmaInfo = {
      ...(btbItem.hour4EmaInfo || {})
    }
    delete hour4EmaInfo.emInfoList
    btbItem.hour4EmaInfo = hour4EmaInfo
    const hourEmaInfo = {
      ...(btbItem.hourEmaInfo || {})
    }
    delete hourEmaInfo.emInfoList
    btbItem.hourEmaInfo = hourEmaInfo
    const minute15EmaInfo = {
      ...(btbItem.minute15EmaInfo || {})
    }
    delete minute15EmaInfo.emInfoList
    btbItem.minute15EmaInfo = minute15EmaInfo
    const code = `${btbItem.code}USDT`
    const daily = futuresDailyData[code]
    if (daily) {
      btbItem.close = daily.lastPrice
      btbItem.volume = daily.volume
      btbItem.quoteVolume = daily.quoteVolume
    }
  })

  const count = await BtbIndex.count(queryNew)
  return {
    list: list.map((v) => {
      return {
        _s: v.code,
        ...v
      }
    }),
    count: count
  }
}

// 添加
exports.addBtbIndex = async function (data) {
  // 现货
  const res = await webDataUtil.binance('getExchangeInfo')
  const resData = res.data || {}
  const symbols = resData.symbols || []
  const exInfo = btbAs.getEXInfo(symbols, data.code)
  // 合约
  const fres = await webDataUtil.binance('getFuturesExchangeInfo')
  const fresData = fres.data || {}
  const fsymbols = fresData.symbols || []
  const fexInfo = btbAs.getEXInfo(fsymbols, data.code)
  if (!(exInfo.maxQty || fexInfo.maxQty)) {
    throw new Error('不存在')
  }
  const oldItem = await BtbIndex.findOne({
    code: data.code
  })
  const updateData = {
    min_qty: parseValue(exInfo.minQty) || 0,
    max_qty: parseValue(exInfo.maxQty) || 0,
    step_size: parseValue(exInfo.stepSize) || 0,
    min_notional: parseValue(exInfo.minNotional) || 0,
    tick_size: parseValue(exInfo.tickSize) || 0,
    f_min_qty: parseValue(fexInfo.minQty) || 0,
    f_max_qty: parseValue(fexInfo.maxQty) || 0,
    f_step_size: parseValue(fexInfo.stepSize) || 0,
    f_min_notional: parseValue(fexInfo.notional) || 0,
    f_tick_size: parseValue(fexInfo.tickSize) || 0
  }
  if (oldItem && oldItem.code) {
    return BtbIndex.update({
      code: data.code
    }, {
      status: 1,
      ...updateData
    })
  } else {
    return BtbIndex.newAndSave({
      ...data,
      ...updateData
    })
  }
}

exports.deleteBtbIndex = async function (data) {
  return BtbIndex.delete({
    _id: data._id
  })
}

// 修改
exports.updateBtbIndex = async function (data) {
  const newData = {
    ...data
  }
  delete newData._id
  return BtbIndex.update({
    _id: data._id
  }, {
    ...newData
  })
}

// 修改
exports.updateBtbIndexByCode = async function (data) {
  const newData = {
    ...data
  }
  delete newData.code
  return BtbIndex.update({
    code: data.code
  }, {
    ...newData
  })
}

// 修改
exports.updateBtbBase = async function () {
  // const list = ['BTC', 'ETH', 'BNB']
  // const res = await webDataUtil.binance('getExchangeInfo')
  // const resData = res.data || {}
  // const symbols = resData.symbols || []
  // for (let i = 0; i < list.length; i++) {
  //   const code = list[i]
  //   const exInfo = btbAs.getEXInfo(symbols, code)
  //   await BtbIndex.update({
  //     code: code
  //   }, {
  //     min_qty: parseValue(exInfo.minQty),
  //     max_qty: parseValue(exInfo.maxQty),
  //     step_size: parseValue(exInfo.stepSize),
  //     min_notional: parseValue(exInfo.minNotional)
  //   })
  // }
}

// 修改状态
exports.changeBtbIndexStatus = async function (data) {
  return BtbIndex.update({
    code: data.code
  }, {
    status: data.status,
    xh_status: data.xh_status,
    hy_status: data.hy_status
  })
}

exports.getBtbIndexByCode = async function (query) {
  return BtbIndex.findOne(query)
}

exports.getBtbIndexWithPosition = async function (query) {
  const futuresAccount = await binanceProxy.getFuturesAccount()
  const detail = await BtbIndex.findOne(query)
  const symbol = `${detail.code}USDT`
  const positionInfo = futuresAccount.positionMap[symbol]
  return {
    detail,
    positionInfo
  }
}

exports.getBtbIndexAll = async function (query) {
  const list = await BtbIndex.find(query)
  return list.map((v) => {
    return {
      _id: v._id,
      code: v.code,
      blacklist: v.blacklist || 0
    }
  })
}

exports.getBtbRecordByPage = async function (query, paging) {
  // 新创建的在前面
  const opt = {
    skip: paging.start,
    limit: paging.offset,
    sort: '-create_at'
  }
  const queryNew = {}
  if (query.code) {
    queryNew.code = query.queryNew
  }
  const data = await Promise.all([
    BtbRecord.find(queryNew, opt),
    BtbRecord.count(queryNew)
  ])
  return { list: data[0], count: data[1] }
}

exports.getBtbMonitor = async function () {
  return BtbMonitor.find()
}

exports.getUsdtBtbIndex = async function () {
  const pricesRes = await webDataUtil.binance('getPrices')
  const priceMap = pricesRes.data || {}
  let list = []
  for (let key in priceMap) {
    if (
      key.indexOf('USDT') !== -1 &&
      key.indexOf('USDT') !== 0 &&
      key.endsWith('USDT')
    ) {
      if (key.indexOf('DOWN') === -1) {
        // list.push(key)
        list.push(key.substring(0, key.lastIndexOf('USDT')))
      }
    }
  }
  return list
}

const indexPoolLength = 20

exports.findGoodBtbInMarket = async function () {
  const futuresDailyRes = await webDataUtil.binance('getFuturesDaily')
  const futuresDailyData = futuresDailyRes.data || {}
  const list = []
  for (let key in futuresDailyData) {
    if (
      key.indexOf('USDT') !== -1 &&
      key.indexOf('USDT') !== 0 &&
      key.endsWith('USDT')
    ) {
      if (
        key.indexOf('DOWN') === -1 &&
        !(/\d/.test(key)) &&
        ['ETHUSDT', 'BTCUSDT'].indexOf(key) === -1
      ) {
        list.push(futuresDailyData[key])
      }
    }
  }
  list.sort((a, b) => {
    const aq = parseFloat(a.quoteVolume)
    const bq = parseFloat(b.quoteVolume)
    return bq - aq
  })
  const upList = []
  // 如果之前没有，就加入进观察池
  for (let i = 0; i < list.length; i++) {
    // 20个入池
    if (i < indexPoolLength) {
      const v = list[i]
      const code = v.symbol.replace('USDT', '')
      v.code = code
      const oldItem = await BtbIndex.findOne({
        code: code
      })
      // 之前没有
      if (!(oldItem && oldItem.code)) {
        let klineRes = await webDataUtil.getFuturesKLinesAll({
          name: code,
          interval: '15m',
          count: 200
        })
        if (klineRes.length > 100) {
          upList.push(v)
          await this.addBtbIndex({
            name: code,
            code: code,
            proportion: 1,
            frequency: 'H',
            status: 1,
            buy_status: 1,
            sell_status: 1,
            condition_buy_status: 0,
            condition_sell_status: 0,
            condition_buy_number: 1,
            condition_sell_number: 1,
            strategy: '4',
            xh_status: 0,
            hy_status: 0
          })
        }
        klineRes = null
      }
    }
  }
  // 达标就打开合约交易
  for (let i = 0; i < list.length; i++) {
    // 20个入池
    if (i < indexPoolLength) {
      const v = list[i]
      let oldItem = await BtbIndex.findOne({
        code: v.code
      })
      if (oldItem && oldItem.code) {
        const close = parseFloat(v.lastPrice)
        const minP = (oldItem.f_min_qty * close) || 0
        const minNotional = oldItem.f_min_notional > minP ? oldItem.f_min_notional : minP
        if (minNotional > appConfig.btb.minNotionalLine) {
          // 太多了
        } else {
          await BtbIndex.update({
            code: v.code
          }, {
            status: 1,
            hy_status: 1
          })
        }
      }
      oldItem = null
    }
  }
  const qList = []
  for (let key in futuresDailyData) {
    if (
      key.indexOf('USDT') !== -1 &&
      key.indexOf('USDT') !== 0 &&
      key.endsWith('USDT')
    ) {
      if (
        key.indexOf('DOWN') === -1 &&
        !(/\d/.test(key))
      ) {
        qList.push(futuresDailyData[key])
      }
    }
  }
  qList.sort((a, b) => {
    const aq = parseFloat(a.quoteVolume)
    const bq = parseFloat(b.quoteVolume)
    return bq - aq
  })
  let sumValue = 0
  qList.forEach((v, index) => {
    if (index < appConfig.btb.len) {
      sumValue += parseFloat(numberUtil.keepFourDecimals(parseFloat(v.quoteVolume) / 100000000))
    }
  })
  await QuoteVolume.newAndSave({
    time: moment().format('YYYY-MM-DD HH:mm'),
    value: sumValue
  })
  return upList
}

function getAstItem (list, code) {
  for (let i = 0; i < list.length; i++) {
    if (list[i].symbol === `${code}USDT`) {
      return list[i]
    }
  }
  return false
}

function canOpenBtbBySg (btbItem, sg) {
  sg = sg || 15
  const dayEmaInfo = btbItem.dayEmaInfo || {}
  if (dayEmaInfo.sg && dayEmaInfo.sg >= sg) {
    // 开始反向的也不行
    if (
      (dayEmaInfo.ema10 > dayEmaInfo.ema20 && dayEmaInfo.ema5 > dayEmaInfo.ema10) &&
      dayEmaInfo.kdjDiff > 0 &&
      dayEmaInfo.macd > 0 &&
      btbAs.ifFuncTimes((em) => {
        return em.ema5 > em.ema20 && em.ema10 > em.ema20
      }, dayEmaInfo.emInfoList || [], 10)
    ) {
      return true
    }
    if (
      (dayEmaInfo.ema10 < dayEmaInfo.ema20 && dayEmaInfo.ema5 < dayEmaInfo.ema10) &&
      dayEmaInfo.kdjDiff < 0 &&
      dayEmaInfo.macd < 0 &&
      btbAs.ifFuncTimes((em) => {
        return em.ema5 < em.ema20 && em.ema10 < em.ema20
      }, dayEmaInfo.emInfoList || [], 10)
    ) {
      return true
    }
  }
  return false
}

exports.getBtbIndexWithDailyBalance = async function () {
  // 新创建的在前面
  const opt = {
    lean: true
  }
  const list = await BtbIndex.find({
    status: 1,
    hy_status: 1
  }, opt)
  const futuresDailyRes = await webDataUtil.binance('getFuturesDaily')
  const futuresDailyData = futuresDailyRes.data || {}
  const balanceRes = await webDataUtil.binance('getFuturesAccount')
  const myBalance = balanceRes.data || {}
  const positionsList = myBalance.positions || []
  list.forEach((btbItem) => {
    const code = `${btbItem.code}USDT`
    const daily = futuresDailyData[code]
    if (daily) {
      btbItem.close = daily.lastPrice
      btbItem.volume = daily.volume
      btbItem.quoteVolume = daily.quoteVolume
    }
    const astItem = getAstItem(positionsList, btbItem.code)
    // 很小
    if (Math.abs(parseFloat(astItem.notional)) < 1) {
      astItem.notional = 0
    }
    // notional就是usdt
    btbItem.notional = astItem.notional
  })
  list.sort((a, b) => {
    const aq = parseFloat(a.quoteVolume)
    const bq = parseFloat(b.quoteVolume)
    return bq - aq
  })
  const openCode = []
  list.forEach((btbItem, index) => {
    // 有仓位的得开着
    if (Math.abs(btbItem.notional) >= 1) {
      openCode.push(btbItem.code)
      return true
    }
    // 成交怎么得也得是前20
    if (index < indexPoolLength) {
      if (canOpenBtbBySg(btbItem, 15)) {
        openCode.push(btbItem.code)
        return true
      }
    }
  })
  // 数量不够
  list.forEach((btbItem, index) => {
    if (openCode.length < appConfig.btb.len) {
      if (index < indexPoolLength) {
        // 不存在
        if (openCode.indexOf(btbItem.code) === -1) {
          if (canOpenBtbBySg(btbItem, 10)) {
            openCode.push(btbItem.code)
            return true
          }
        }
      }
    }
  })
  list.forEach((btbItem, index) => {
    if (openCode.length < appConfig.btb.len) {
      if (index < indexPoolLength) {
        // 不存在
        if (openCode.indexOf(btbItem.code) === -1) {
          if (canOpenBtbBySg(btbItem, 5)) {
            openCode.push(btbItem.code)
            return true
          }
        }
      }
    }
  })
  // 数量不够
  list.forEach((btbItem) => {
    if (openCode.length < appConfig.btb.len) {
      // 不存在
      if (openCode.indexOf(btbItem.code) === -1) {
        openCode.push(btbItem.code)
        return true
      }
    }
  })
  for (let i = 0; i < list.length; i++) {
    const v = list[i]
    if (openCode.indexOf(v.code) === -1) {
      await BtbIndex.update({
        code: v.code
      }, {
        status: 0,
        hy_status: 0
      })
    }
  }
}

exports.getQuoteRecordsByDay = async function (data) {
  // 新创建的在前面
  const opt = {
    limit: data.day,
    sort: '-time'
  }
  return QuoteVolume.find({}, opt)
}

exports.reformatQuoteVolumeRecords = async function () {
  const list = await QuoteVolume.find({}, {
    sort: '-time'
  })
  for (let i = 0; i < list.length; i++) {
    const item = list[i]
    const last = list[i + 1] || {}
    if (item && last) {
      await QuoteVolume.update({
        _id: item._id
      }, {
        balance_change: numberUtil.keepSixDecimals(item.total_margin_balance - (last.total_margin_balance || 0))
      })
    }
  }
}

exports.getQuoteVolumeRecordsByPage = async function (query, paging) {
  // 新创建的在前面
  const opt = {
    skip: paging.start,
    limit: paging.offset,
    sort: '-time',
    lean: true
  }
  const queryNew = {}
  if (query.startTime || query.endTime) {
    queryNew.create_at = {}
  }
  if (query.startTime) {
    queryNew.create_at.$gte = query.startTime
  }
  if (query.endTime) {
    queryNew.create_at.$lte = query.endTime
  }
  if (query.modelStr) {
    queryNew.model_str = query.modelStr
  }
  const data = await Promise.all([
    QuoteVolume.find(queryNew, opt),
    QuoteVolume.count(queryNew)
  ])
  return {
    list: data[0],
    count: data[1]
  }
}

exports.getQuoteVolumeRecordDetail = async function (query) {
  return QuoteVolume.findOneById(query._id)
}

exports.getBtbWarnRecordsByPage = async function (query, paging) {
  // 新创建的在前面
  const opt = {
    skip: paging.start,
    limit: paging.offset,
    sort: '-time',
    lean: true
  }
  const data = await Promise.all([
    BtbWarnProxy.find({}, opt),
    BtbWarnProxy.count({})
  ])
  return {
    list: data[0],
    count: data[1]
  }
}

exports.getAssetHY = require('./btb/getAssetHY')

exports.getMyBalanceInfo = require('./btb/getMyBalanceInfo')

exports.updateBtbMonitor = require('./btb/updateBtbMonitor')
exports.checkBtbStatusMonth = require('./btb/checkBtbStatusMonth')
exports.checkBtbStatusWeek = require('./btb/checkBtbStatusWeek')
exports.checkBtbStatusDay = require('./btb/checkBtbStatusDay')
exports.checkBtbStatus4Hour = require('./btb/checkBtbStatus4Hour')
exports.checkBtbStatus1Hour = require('./btb/checkBtbStatus1Hour')

exports.buySellBtbXH15m = require('./btb/buySellBtbXH15m')
exports.buySellBtbHY15m = require('./btb/buySellBtbHY15m')
exports.closePositionHY = require('./btb/closePositionHY')
exports.closePositionXH = require('./btb/closePositionXH')
exports.quickBuyXH = require('./btb/quickBuyXH')
exports.buySellBtbHY15mV2 = require('./btb/buySellBtbHY15mV2')
exports.findGoodBtbInMarketV2 = require('./btb/findGoodBtbInMarketV2')
exports.analysisBtbTest = require('./btb/analysisBtbTest')
exports.getMyBalanceInfoV2 = require('./btb/getMyBalanceInfoV2')
exports.preFindGoodBtbInMarket = require('./btb/preFindGoodBtbInMarket')
exports.analysisBtbBuySellTest = require('./btb/analysisBtbBuySellTest')
exports.closePositionHYV2 = require('./btb/closePositionHYV2')
exports.changeLeverage = require('./btb/changeLeverage')
exports.findBtbByAnalysis = require('./btb/findBtbByAnalysis')
